require("xml")
require("framexml")

local exec = os.execute
local format = string.format
local dump = string.dump
local load = load
local open = io.open
local popen = io.popen
local loadstring = loadstring
local parsefile = xml.parsefile
local convert = framexml.convert
local NO_CACHE = config.NO_CACHE
local BASE_PATH = config.BASE_PATH

local lfs
do
	local status
	status, lfs = pcall(require, "lfs")
	if not status then
		lfs = nil
	end
end

local log = log
local lualint = lualint
local xmlvalid
if config.XMLVALID then
	xmlvalid = function (src)
		if src:find("Bindings.xml", nil, true) then return end
		local f = popen(format("xjparse -f -S \"%s\" \"%s\" 2>&1", BASE_PATH.."/FrameXML/UI.xsd", src), "r")
		local result = f:read("*a")
		f:close()
		if not result:find("with no errors and no warnings", nil, true) then
			return result
		end
	end
end

module("cache")

local is_newer_than
if lfs then
	is_newer_than = function (file1, file2)
		return (lfs.attributes(file1, "change") or 0) > (lfs.attributes(file2, "change") or 0)
	end
else
	is_newer_than = function (file1, file2)
		return exec(format("test %s -nt %s", file1, file2)) == 0
	end
end

local function get_file_name(file)
	local l = BASE_PATH:len()
	if file:sub(1, l) == BASE_PATH then
		if file:sub(l + 1, l + 1) == "/" then
			l = l + 1
		end
		return file:sub(l + 1)
	end
	return file
end

local function real_loadfile(file)
	if lualint then lualint.lint(file, function (s, ...) log("\t"..s, ...) end) end
	local f, err = open(file, "r")
	if not f then
		return nil, err
	end
	local first = true
	return load(function ()
			if first then
				first = false
				local line = f:read()
				if not line then
					return
				elseif line:sub(1, 3) == "\239\187\191" then
					return line:sub(4).."\n"
				else
					return line.."\n"
				end
			else
				local line = f:read()
				if line then
					return line.."\n"
				end
			end
		end, get_file_name(file))
end

local function real_loadxml(file)
	local errors = xmlvalid and xmlvalid(file)
	if errors then
		log("II Schema validation errors for %s :\n%s", file, errors)
	end
	local tree, err = parsefile(file)
	if not tree then return nil, err end
	local data = convert(file, tree)
	return loadstring(data, get_file_name(file))
end

local function cached_load(file, nocache, load)
	local cached = "cache/"..file:gsub("[/\\%.]", "_")
	if not NO_CACHE and not nocache and is_newer_than(cached, file) then
		local f, err = open(cached, "rb")
		if not f then
			return nil, err
		end
		local s = f:read("*a")
		f:close()
		local result, err = loadstring(s, file)
		if not result then
			return nil, err
		end
		return result, true
	else
		local result, err = load(file)
		if not result then
			return nil, err
		end
		if not nocache then
			local f, err = open(cached, "wb")
			if not f then
				return nil, err
			end
			f:write(dump(result))
			f:close()
		end
		return result
	end
end


function loadfile(file, nocache)
	return cached_load(file, nocache, real_loadfile)
end

function loadxml(file, nocache)
	return cached_load(file, nocache, real_loadxml)
end
